/*
 * Created on Jul 22, 2008
 *
 * To change the template for this generated file go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
package ro.qi.framework.sys.users.bo;

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.util.Calendar;
import java.util.Date;

import ro.qi.framework.sys.debug.Logger;
import ro.qi.framework.sys.users.entities.UsersAccount;
import ro.qi.framework.sys.users.entities.UsersAccountFacebook;
import ro.qi.framework.sys.users.exceptions.UsersAccountAccountNotFoundException;
import ro.qi.framework.sys.users.exceptions.UsersAccountAuthenticateAccountBlockedException;
import ro.qi.framework.sys.users.exceptions.UsersAccountAuthenticateAccountDeletedException;
import ro.qi.framework.sys.users.exceptions.UsersAccountAuthenticateAccountNotValidatedException;
import ro.qi.framework.sys.users.exceptions.UsersAccountAuthenticateInvalidPasswordException;
import ro.qi.framework.sys.users.exceptions.UsersAccountAuthenticateLoginEmptyException;
import ro.qi.framework.sys.users.exceptions.UsersAccountAuthenticateLoginNullException;
import ro.qi.framework.sys.users.exceptions.UsersAccountAuthenticatePasswordEmptyException;
import ro.qi.framework.sys.users.exceptions.UsersAccountAuthenticatePasswordNullException;
import ro.qi.framework.sys.users.exceptions.UsersAccountException;
import ro.qi.framework.sys.users.exceptions.UsersAccountRegisterEmailAlreadyExistException;
import ro.qi.framework.sys.users.exceptions.UsersAccountRegisterEmailEmptyException;
import ro.qi.framework.sys.users.exceptions.UsersAccountRegisterEmailNotMatchException;
import ro.qi.framework.sys.users.exceptions.UsersAccountRegisterEmailNotWellFormedException;
import ro.qi.framework.sys.users.exceptions.UsersAccountRegisterEmailNullException;
import ro.qi.framework.sys.users.exceptions.UsersAccountRegisterLoginAlreadyExistException;
import ro.qi.framework.sys.users.exceptions.UsersAccountRegisterLoginEmptyException;
import ro.qi.framework.sys.users.exceptions.UsersAccountRegisterLoginNotWellFormedException;
import ro.qi.framework.sys.users.exceptions.UsersAccountRegisterLoginNullException;
import ro.qi.framework.sys.users.exceptions.UsersAccountRegisterPasswordEmptyException;
import ro.qi.framework.sys.users.exceptions.UsersAccountRegisterPasswordNotMatchException;
import ro.qi.framework.sys.users.exceptions.UsersAccountRegisterPasswordNullException;
import ro.qi.framework.sys.util.string.ByteToHex;
import ro.qi.framework.sys.util.string.RandomString;

/**
 * @author George Pogorelschi
 *
 * To change the template for this generated type comment go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
public class UsersBOAccount {
	
	public String DIGEST_ALGORITHM_MD5				= "MD5";
	public String DIGEST_ALGORITHM_SHA1				= "SHA-1";
	
	private UsersAccount usersAccount = null;
	
	private boolean encryptPassword 				= true;

	private boolean isFirstNameRequired 			= false;
	private boolean isFamilyNameRequired 			= false;
	private boolean isEmailConfirmationRequired 	= false;
	
	private int id									= 0;
	private String login							= null;
	private String email							= null;
	private String confirmEmail						= null;
	private String currentPassword					= null;
	private String password							= null;
	private String confirmPassword					= null;
	private String firstName						= null;
	private String familyName						= null;
	private String remoteIpAddress					= null;
	
	private int passwordSaltLength					= 64;
	
	private String digestAlgorithm					= DIGEST_ALGORITHM_MD5;

	public UsersBOAccount() {
		usersAccount = new UsersAccount();
	}

	public UsersBOAccount(int id) {
		usersAccount = new UsersAccount(id);
	}

	public UsersBOAccount(String login) {
		usersAccount = new UsersAccount(login);
	}

	public UsersBOAccount(String login, String email) {
		usersAccount = new UsersAccount(login, email);
	}

	/**
	 * @param account
	 * 
	 * 
	 */
	public void registerAccount() throws UsersAccountException, UnsupportedEncodingException {

		if (getLogin() == null) {
			Logger.debug(this, "Register account failed: login is null");
			throw new UsersAccountRegisterLoginNullException();
		} else {
			if (getLogin().trim().length() == 0) {
				Logger.debug(this, "Register account failed: login is empty");
				throw new UsersAccountRegisterLoginEmptyException();
			} else {
				if (!validateLogin(getLogin())) {
					Logger.debug(this, "Register account failed: login is not well formed");
					throw new UsersAccountRegisterLoginNotWellFormedException();
				}
			}
		}

		// check for duplicate login
		if (userExists(getLogin())) {
			Logger.debug(this, "Register account failed: login already exist");
			throw new UsersAccountRegisterLoginAlreadyExistException();
		}

		if (getEmail() == null) {
			Logger.debug(this, "Register account failed: email is null");
			throw new UsersAccountRegisterEmailNullException();
		} else {
			if (getEmail().trim().length() == 0) {
				Logger.debug(this, "Register account failed: login is empty");
				throw new UsersAccountRegisterEmailEmptyException();
			} else {
				if (!validateEmail(getEmail())) {
					Logger.debug(this, "Register account failed: email is not well formed");
					throw new UsersAccountRegisterEmailNotWellFormedException();
				}
			}
		}

		// check for duplicate email
		if (emailExists(getEmail())) {
			Logger.debug(this, "Register account failed: email already exist");
			throw new UsersAccountRegisterEmailAlreadyExistException();
		}
		
		// validate email
		if (!getEmail().equals(getConfirmEmail())) {
			Logger.debug(this, "Register account failed: email and confirm email don't match");
			throw new UsersAccountRegisterEmailNotMatchException();
		}

//		if (getConfirmEmail() == null) {
//			Logger.debug(this, "Register account failed: confirm email is null");
//			throw new UserAccountException();
//		}

		if (getPassword() == null) {
			Logger.debug(this, "Register account failed: password is null");
			throw new UsersAccountRegisterPasswordNullException();
		} else {
			if (getPassword().trim().length() == 0) {
				Logger.debug(this, "Register account failed: password is empty");
				throw new UsersAccountRegisterPasswordEmptyException(100, "Register account failed: password is empty");
//			} else {
//				if (!validateEmail(getEmail())) {
//					Logger.debug(this, "Register account failed: email is not well formed");
//					throw new UserAccountRegisterEmailNotWellFormedException();
//				}
			}
		}

//		if (getConfirmPassword() == null) {
//			Logger.debug(this, "Register account failed: confirm password is null");
//			throw new UserAccountException();
//		}

		// validate password
		if (!getPassword().equals(getConfirmPassword())) {
			Logger.debug(this, "Register account  failed: password and confirm password don't match");
			throw new UsersAccountRegisterPasswordNotMatchException();
		}
		
		if (isFirstNameRequired()) {
			// validate first name
			if (getFirstName() == null) {
				Logger.debug(this, "Register account failed: first name is null");
				throw new UsersAccountException();
			} else {
				if (getFirstName().trim().length() == 0) {
					Logger.debug(this, "Register account failed: first name is zero length");
					throw new UsersAccountException();
				}
			}
		}

		if (isFamilyNameRequired()) {
			// validate family name
			if (getFamilyName() == null) {
				Logger.debug(this, "Register account failed: first name is null or empty");
				throw new UsersAccountException();
			} else {
				if (getFamilyName().trim().length() == 0) {
					Logger.debug(this, "Register account failed: family name is zero length");
					throw new UsersAccountException();
				}
			}
		}

		usersAccount.setLogin(getLogin());
		if (isEncryptPassword()) {
			String passwordSalt = generatePasswordSalt(passwordSaltLength);
			String encryptedPassword = encryptPassword(getPassword(), passwordSalt);
			usersAccount.setPassword(encryptedPassword);
			usersAccount.setPasswordSalt(passwordSalt);
		} else {
			usersAccount.setPassword(getPassword());
		}
		usersAccount.setEmail(getEmail());
		usersAccount.setFirstName(getFirstName());
		usersAccount.setFamilyName(getFamilyName());
		usersAccount.setCreatedIpAddress(getRemoteIpAddress());
		if (isEmailConfirmationRequired) {
			usersAccount.setStatus(UsersAccount.STATUS_NOT_VALIDATED);
		} else {
			usersAccount.setStatus(UsersAccount.STATUS_VALID);
		}
		usersAccount.add();

	}


	/**
	 * @param account
	 * 
	 * 
	 * 
	 */
	public int userLogin() throws UsersAccountException, UnsupportedEncodingException {

		if (getLogin() == null) {
			Logger.debug(this, "Authentication failed: login is null");
			throw new UsersAccountAuthenticateLoginNullException();
		} else {
			if (getLogin().trim().length() == 0) {
				Logger.debug(this, "Authentication failed: login is empty");
				throw new UsersAccountAuthenticateLoginEmptyException();
//			} else {
//				if (!validateLogin(getLogin())) {
//					Logger.debug(this, "Authentication failed: login is not well formed");
////					throw new UserAccountRegisterLoginNotWellFormedException();
//				}
			}
		}
		
		if (getPassword() == null) {
			Logger.debug(this, "Login failed: password is null");
			throw new UsersAccountAuthenticatePasswordNullException();
		} else if (getPassword().trim().length() == 0) {
			Logger.debug(this, "Login failed: password is empty");
			throw new UsersAccountAuthenticatePasswordEmptyException();
		}

		usersAccount.setLogin(getLogin());
		usersAccount.select();

		if (usersAccount.getId() == 0) {
			usersAccount.setEmail(getLogin());
			usersAccount.select();
			if (usersAccount.getId() == 0) {
				Logger.debug(this, "Login failed: account not found");
				throw new UsersAccountAccountNotFoundException();
			}
		}

		if (usersAccount.getStatus() == UsersAccount.STATUS_NOT_VALIDATED) {
			Logger.debug(this, "Login failed: account not validated");
			throw new UsersAccountAuthenticateAccountNotValidatedException();
		}

		if (usersAccount.getStatus() == UsersAccount.STATUS_BLOCKED) {
			Logger.debug(this, "Login failed: Account blocked");
			throw new UsersAccountAuthenticateAccountBlockedException();
		}

		if (usersAccount.getStatus() == UsersAccount.STATUS_DELETED) {
			Logger.debug(this, "Login failed: Account deleted");
			throw new UsersAccountAuthenticateAccountDeletedException();
		}

		if (isEncryptPassword()) {
			if (usersAccount.getPassword().equals(encryptPassword(getPassword(), usersAccount.getPasswordSalt()))) {
				usersAccount.setState(UsersAccount.STATE_ONLINE);
				usersAccount.update();
				// TODO: update user accounts access log
				return usersAccount.getId();
			} else {
				Logger.info(this, "Login failed: Password not matched");
				throw new UsersAccountAuthenticateInvalidPasswordException();
			}
		} else {
			if (usersAccount.getPassword().equals(getPassword())) {
				usersAccount.setState(UsersAccount.STATE_ONLINE);
				usersAccount.update();
				// TODO: update user accounts access log
				return usersAccount.getId();
			} else {
				Logger.info(this, "Login failed: Password not matched");
				throw new UsersAccountAuthenticateInvalidPasswordException();
			}
		}
	}

	/*
	 * 
	 */
	public int facebookLogin(String facebookId) throws UsersAccountException {
		UsersAccountFacebook facebookAccount = new UsersAccountFacebook(facebookId);
		
		usersAccount.setId(facebookAccount.getId());
		usersAccount.select();

		if (usersAccount.getId() > 0) {
			return usersAccount.getId();
		} else {
			throw new UsersAccountException();
		}
	}

	/*
	 * 
	 */
	public void linkFacebook(String facebookId) throws UsersAccountException {
		UsersAccountFacebook facebookAccount = new UsersAccountFacebook();
		facebookAccount.setId(getId());
		facebookAccount.setFacebookId(facebookId);
		facebookAccount.add();
	}
	
	public void unlinkFacebook(String facebookId) throws UsersAccountException {
		UsersAccountFacebook facebookAccount = new UsersAccountFacebook();
		facebookAccount.setId(getId());
		facebookAccount.setFacebookId(facebookId);
		facebookAccount.delete();
	}

	/**
	 * @param account
	 */
	public void userLogout(int userId, String session) {
		UsersAccount userAccount = new UsersAccount(userId);
		userAccount.setState(UsersAccount.STATE_OFFLINE);
		userAccount.update();
		// TODO: update user accounts access log
	}
	
	public void changePassword(String currentPassword, String newPassword, String confirmNewPassword) throws UsersAccountException, UnsupportedEncodingException {

		if (newPassword.equals(confirmNewPassword)) {
			if (isEncryptPassword()) {
				String passwordSalt = generatePasswordSalt(passwordSaltLength);
				if (encryptPassword(currentPassword, usersAccount.getPasswordSalt()).equals(usersAccount.getPassword())) {
					usersAccount.setPassword(encryptPassword(newPassword, passwordSalt));
					usersAccount.setPasswordSalt(passwordSalt);
					usersAccount.update();
				} else {
					System.out.println("currentPassword: " + currentPassword);
					System.out.println("encrypted encryptPassword(currentPassword): " + encryptPassword(currentPassword, passwordSalt));
					System.out.println("usersAccount.getPassword(): " + usersAccount.getPassword());
					Logger.info(this, "Change password failed: The current password didn't match");
					throw new UsersAccountException();
				}
				
			} else {

				if (currentPassword.equals(usersAccount.getPassword())) {
					usersAccount.setPassword(newPassword);
					usersAccount.update();
				} else {
					System.out.println("currentPassword: " + currentPassword);
					System.out.println("usersAccount.getPassword(): " + usersAccount.getPassword());
					Logger.info(this, "Change password failed: The current password didn't match");
					throw new UsersAccountException();
				}

			}
		} else {
			Logger.info(this, "Change password failed: Password and confirm password don't match");
			throw new UsersAccountException();
		}
	}

	public void changeEmail(String currentEmail, String newEmail, String confirmNewEmail) throws UsersAccountException {

		if (newEmail.equals(confirmNewEmail)) {
			if (currentEmail.equals(usersAccount.getEmail())) {
				usersAccount.setEmail(newEmail);
				usersAccount.update();
			} else {
				System.out.println("currentEmail: " + currentEmail);
				System.out.println("usersAccount.getEmail(): " + usersAccount.getEmail());
				Logger.info(this, "Change email failed: The current email didn't match");
				throw new UsersAccountException();
			}
		} else {
			Logger.info(this, "Change email failed: Password and confirm email don't match");
			throw new UsersAccountException();
		}
	}

	/**
	 * @param account
	 * 
	 * 
	 * 
	 */
	public String generatePassword(int length) throws UsersAccountException, UnsupportedEncodingException {

//		usersAccount.setLogin(getLogin());
//		usersAccount.select();

		setPassword(new RandomString(length).getRandomString());

		if (usersAccount.getId() > 0) {
			if (isEncryptPassword()) {
				String passwordSalt = generatePasswordSalt(passwordSaltLength);
				usersAccount.setPassword(encryptPassword(getPassword(), passwordSalt));
				usersAccount.setPasswordSalt(passwordSalt);
			} else {
				usersAccount.setPassword(getPassword());
			}
			usersAccount.update();
		} else {
			Logger.debug(this, "Generate password failed: account not found");
			throw new UsersAccountAccountNotFoundException("Generate password failed: account not found");
		}

		return getPassword();
	}
	
	public String generatePasswordSalt(int length) {
		return new RandomString(length).getRandomString();
	}
	
	public boolean isBlocked() {
		return usersAccount.getStatus() == UsersAccount.STATUS_DELETED;
	}

	// tools
	public void block() {
		usersAccount.setStatus(UsersAccount.STATUS_DELETED);
		usersAccount.update();
	}

	public void unblock() {
		usersAccount.setStatus(UsersAccount.STATUS_VALID);
		usersAccount.update();
	}

	public int getBirthdateDay() {
		Calendar cal = Calendar.getInstance();
		cal.setTime(usersAccount.getBirthdate());
		return cal.get(Calendar.DAY_OF_MONTH);
	}

	public int getBirthdateMonth() {
		Calendar cal = Calendar.getInstance();
		cal.setTime(usersAccount.getBirthdate());
		return cal.get(Calendar.MONTH);
	}

	public int getBirthdateYear() {
		Calendar cal = Calendar.getInstance();
		cal.setTime(usersAccount.getBirthdate());
		return cal.get(Calendar.YEAR);
	}

	/**
	 * 
	 */
	public String getFullName() {
		StringBuffer fullName = new StringBuffer();
		
		if (usersAccount.getFirstName().trim().length() > 0) {
			fullName.append(usersAccount.getFirstName() + " ");
		}
		
		if (usersAccount.getMiddleName().trim().length() > 0) {
			fullName.append(usersAccount.getMiddleName() + " ");
		}
		
		if (usersAccount.getFamilyName().trim().length() > 0) {
			fullName.append(usersAccount.getFamilyName());
		}
		
		return fullName.toString().trim();
	}

	/**
	 * @param password
	 */
	public String encryptPassword(String password, String passwordSalt) throws UnsupportedEncodingException {

		if (passwordSalt != null) {
			password = password + passwordSalt;
		}

		java.security.MessageDigest algorithm = null;

		try {
			algorithm = java.security.MessageDigest.getInstance(getDigestAlgorithm());
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}

		algorithm.reset();
		algorithm.update(password.getBytes());

		byte[] digest = algorithm.digest();

		return ByteToHex.getHexString(digest);
	}

	
	public String toHexString(byte[] in){
		BigInteger temp = new BigInteger(in);
		return temp.toString(16);
	}

	/**
	 * @param login
	 */
	public boolean validateLogin(String login) {
		// TODO: validate login format
		return true;	
	}

	/**
	 * @param email
	 */
	public boolean validateEmail(String email) {
		// TODO: validate email format
//		String[] tokens = email.split("@");
//		return (tokens.length == 2 && tokens[0].length() > 0 && tokens[1].length() > 0);
		return true;
	}

	/**
	 * @param value
	 */
	public void setEncryptPassword(boolean value) {
		encryptPassword = value;
	}

	/**
	 * @param login
	 */
	public static boolean userExists(String login) {
		return new UsersAccount(login).getId() > 0;
	}

	/**
	 * @param email
	 */
	public static boolean emailExists(String email) {
		return new UsersAccount(null, email).getId() > 0;
	}

	/**
	 * @param b
	 */
	public void setFirstNameRequired(boolean value) {
		isFirstNameRequired = value;
	}

	/**
	 * @return
	 */
	public boolean isFirstNameRequired() {
		return isFirstNameRequired;
	}

	/**
	 * @param b
	 */
	public void setFamilyNameRequired(boolean value) {
		isFamilyNameRequired = value;
	}

	/**
	 * @return
	 */
	public boolean isFamilyNameRequired() {
		return isFamilyNameRequired;
	}

	/**
	 * @param string
	 */
	public void setLogin(String string) {
		login = string;
	}

	/**
	 * @return
	 */
	public String getLogin() {
		return login;
	}

	/**
	 * @param string
	 */
	public void setEmail(String string) {
		email = string;
	}

	/**
	 * @return
	 */
	public String getEmail() {
		return email;
	}

	/**
	 * @param string
	 */
	public void setPassword(String string) {
		password = string;
	}

	/**
	 * @return
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * @return
	 */
	public String getFamilyName() {
		return familyName;
	}

	/**
	 * @return
	 */
	public String getFirstName() {
		return firstName;
	}

	/**
	 * @return
	 */
	public String getConfirmEmail() {
		return confirmEmail;
	}

	/**
	 * @return
	 */
	public String getConfirmPassword() {
		return confirmPassword;
	}

	/**
	 * @param string
	 */
	public void setFamilyName(String string) {
		familyName = string;
	}

	/**
	 * @param string
	 */
	public void setFirstName(String string) {
		firstName = string;
	}

	/**
	 * @param string
	 */
	public void setConfirmEmail(String string) {
		confirmEmail = string;
	}

	/**
	 * @param string
	 */
	public void setConfirmPassword(String string) {
		confirmPassword = string;
	}

	/**
	 * @return
	 */
	public String getRemoteIpAddress() {
		return remoteIpAddress;
	}

	/**
	 * @param string
	 */
	public void setRemoteIpAddress(String string) {
		remoteIpAddress = string;
	}

	/**
	 * @return
	 */
	public boolean isEmailConfirmationRequired() {
		return isEmailConfirmationRequired;
	}

	/**
	 * @param b
	 */
	public void setEmailConfirmationRequired(boolean b) {
		isEmailConfirmationRequired = b;
	}

	/**
	 * @return
	 */
	public boolean isEncryptPassword() {
		return encryptPassword;
	}

	/**
	 * @return
	 */
	public int getId() {
		return usersAccount.getId();
	}

	/**
	 * @return
	 */
	public UsersAccount getUsersAccount() {
		return usersAccount;
	}

	public UsersAccount getAccount() {
		return usersAccount;
	}

	/**
	 * @param i
	 */
	public void setId(int id) {
		usersAccount.setId(id);
	}

	/**
	 * 
	 */
	public String getGender() {
		return usersAccount.getGender();
	}

	/**
	 * 
	 */
	public Date getBirthdate() {
		return usersAccount.getBirthdate();
	}

	public int getCountryId() {
		return usersAccount.getCountryId();
	}
	
	public int getCountyId() {
		return usersAccount.getCountyId();
	}
	
	public String getPostalCode() {
		return usersAccount.getPostalCode();
	}
	
	public String getOfficePhone() {
		return usersAccount.getOfficePhone();
	}
	
	public String getMobilePhone() {
		return usersAccount.getMobilePhone();
	}
	
	/**
	 * @param account
	 * @deprecated
	 */
	public void setUserAccount(UsersAccount account) {
		this.usersAccount = account;
	}

	public void add() {
		this.usersAccount.add();
	}

	public void update() {
		this.usersAccount.update();
	}
	
	public void select() {
		this.usersAccount.select();
	}
	
	public void delete() {
		this.usersAccount.delete();
	}


	/**
	 * @return
	 */
	public String getDigestAlgorithm() {
		return digestAlgorithm;
	}

	/**
	 * @param string
	 */
	public void setDigestAlgorithm(String string) {
		digestAlgorithm = string;
	}

	public void setGender(String value) {
		this.usersAccount.setGender(value);
	}

	public void setPostalCode(String value) {
		this.usersAccount.setPostalCode(value);
	}

	public void setBirthdate(Date value) {
		this.usersAccount.setBirthdate(value);
	}

	public void setCountryId(int value) {
		this.usersAccount.setCountryId(value);
	}

	public void setCountyId(int value) {
		this.usersAccount.setCountyId(value);
	}

	public void setCity(String value) {
		this.usersAccount.setCity(value);
	}

	public void setOfficePhone(String value) {
		this.usersAccount.setOfficePhone(value);
	}

	public void setMobilePhone(String value) {
		this.usersAccount.setMobilePhone(value);
	}

}
